多線程的狀態(tài):

創(chuàng)建線程的第二種方式:實現(xiàn)Runnable接口
1 定義類實現(xiàn)Runnable接口
2 覆蓋接口中的run方法,將線程的任務(wù)代碼封裝到run方法中
3 通過Thread類創(chuàng)建線程對象,并將Runnable接口的子類對象作為Thread類的構(gòu)造函數(shù)的參數(shù)進(jìn)行傳遞
為什么?因為線程的任務(wù)都封裝在Runnable接口子類對象的run方法中。
所以要在線程對象創(chuàng)建時就必須明確要運(yùn)行的任務(wù)。
4 調(diào)用線程對象的strat方法開啟線程
Runnable的出現(xiàn)僅僅是將線程的任務(wù)進(jìn)行了對象的封裝。
實現(xiàn)Runnable接口的好處:
1 將線程的任務(wù)從線程的子類中分離出來,進(jìn)行單獨(dú)的封裝,
按照面向?qū)ο蟮乃枷雽⑷蝿?wù)封裝成對象。
2 避免了java單繼承的局限性
所以,創(chuàng)建線程的第二種方式比較常見。
class Demo implements Runnable{//準(zhǔn)備擴(kuò)展Demo類的功能,讓其中的內(nèi)容可以
//作為線程的任務(wù)執(zhí)行,這時就需要通過接口的形式完成
public void run(){
show();
}
public void show(){
for(int x=0;x<10;x++){
System.out.println("....x="+x+"....name="+Thread.currentThread().getName());
}
}
}
public class RunnableDemo {
public static void main(String[] args) {
Demo d=new Demo();
Thread t1=new Thread(d);
Thread t2=new Thread(d);
t1.start();
t2.start();
}
}
運(yùn)行:
多線程的安全問題:
線程可能會出現(xiàn)負(fù)數(shù)還能運(yùn)行的情況,即num=0,1,-1的情況,
class Ticket implements Runnable
{
private int num=20;
public void run(){
while(true){
if(num>0){
try{//因為Runnable中run方法沒有Thows,所以這里只能用try-catch。
Thread.sleep(10);//理想狀態(tài)下不會出現(xiàn),但是不代表不會出現(xiàn),一旦出現(xiàn)就是致命問題,這里用sleep做停頓操作,使問題顯現(xiàn)出來
}catch(InterruptedException e){
}
System.out.println(Thread.currentThread().getName()+"...sale..."+num--);
}
}
}
}
public class LiZi {
public static void main(String[] args) {
Ticket t=new Ticket();//創(chuàng)建一個線程任務(wù)對象
// Ticket tt=new Ticket();//一個對象表示一百張票
Thread t1=new Thread(t);
Thread t2=new Thread(t);
Thread t3=new Thread(t);
Thread t4=new Thread(t);
t1.start();
t2.start();
t3.start();
t4.start();
}
運(yùn)行:
同步代碼塊:
問題出現(xiàn)就需要解決,如何解決呢?
解決思路:就是將多條操作共享數(shù)據(jù)的線程代碼封裝起來,當(dāng)有線程在執(zhí)行這些代碼的時候,其他線程是不可以參與運(yùn)算的。必須要當(dāng)前線程把這些代碼都執(zhí)行完畢后,其他線程才可以參與運(yùn)算。
而在java中用同步代碼塊就可以解決這個問題。
同步代碼塊的格式:
synchronized(對象){//這個對象就是鎖
需要被同步的代碼塊;
}
解決:
class Ticket implements Runnable//extends Thread
{
private int num=20;
Object obj=new Object();//創(chuàng)建什么對象都可以
public void run(){
while(true){
synchronized(obj)//同步代碼塊
{
if(num>0){
try{//線程安全問題:線程可能會出現(xiàn)負(fù)數(shù)還能運(yùn)行的情況,即num=0,1,-1的情況,
Thread.sleep(10);//理想狀態(tài)下不會出現(xiàn),但是不代表不會出現(xiàn),一旦出現(xiàn)就是致命問題,這里用sleep做停頓操作,使問題顯現(xiàn)出來
}catch(InterruptedException e){
}
System.out.println(Thread.currentThread().getName()+"...sale..."+num--);
}
}
}
}
}
public class LiZi {
public static void main(String[] args) {
Ticket t=new Ticket();//創(chuàng)建一個線程任務(wù)對象
// Ticket tt=new Ticket();//一個對象表示一百張票
Thread t1=new Thread(t);
Thread t2=new Thread(t);
Thread t3=new Thread(t);
Thread t4=new Thread(t);
}
同步的好處:
解決了線程的安全問題(解決了一小部分而已)。
同步的弊端:
相對降低了效率,因為同步外的線程都會判斷同步鎖。
同步的前提:
必須有多個線程并使用同一個鎖。
不同鎖的情況:
將Object obj=new Object();放進(jìn)run()方法中,就形成了四個鎖,一個線程一個鎖。
class Ticket implements Runnable//extends Thread
{
private int num=20;
//創(chuàng)建什么對象都可以
public void run(){Object obj=new Object();
while(true){
synchronized(obj)//同步代碼塊 這里也可以直接用 Ticket.class 但最好還是obj的方式
{
if(num>0){
try{//線程安全問題:線程可能會出現(xiàn)負(fù)數(shù)還能運(yùn)行的情況,即num=0,1,-1的情況,
Thread.sleep(10);//理想狀態(tài)下不會出現(xiàn),但是不代表不會出現(xiàn),一旦出現(xiàn)就是致命問題,這里用sleep做停頓操作,使問題顯現(xiàn)出來
}catch(InterruptedException e){
}
System.out.println(Thread.currentThread().getName()+"...sale..."+num--);
}
}
}
}
}
public class LiZi {
public static void main(String[] args) {
Ticket t=new Ticket();//創(chuàng)建一個線程任務(wù)對象
Thread t1=new Thread(t);
Thread t2=new Thread(t);
Thread t3=new Thread(t);
Thread t4=new Thread(t);
t1.start();
t2.start();
t3.start();
t4.start();
}
運(yùn)行:
例子:
/*
需求:儲戶,兩個,每個都到銀行存錢每次存一百,共存三次 。銀行總共收到多少錢
*/
class Bank{
private int sum;//sum也是共享的
private Object obj=new Object();
public void add(int num){//找到安全隱患是在這發(fā)生的
synchronized(obj){
sum=sum+num;
try{
Thread.sleep(10);
}catch(InterruptedException e){}
System.out.println("sum="+sum);
}
}
}
class Cus implements Runnable{
private Bank d =new Bank();//d是共享的
public void run(){
// Bank d =new Bank();不可以在這里,這這里就分成了兩個銀行各收三百
for(int x=0;x<3;x++){
d.add(100);
}
}
}
public class TongBu {
public static void main(String[] args) {
Cus c=new Cus();
Thread t1=new Thread(c);
Thread t2=new Thread(c);
t1.start();
t2.start();
}
}
運(yùn)行:
將bank類改為:
class Bank{
private int sum;//sum也是共享的
//private Object obj=new Object();
public synchronized void add(int num){//找到安全隱患是在這發(fā)生的
//synchronized(obj){
sum=sum+num;
try{
Thread.sleep(10);
}catch(InterruptedException e){}
System.out.println("sum="+sum);
//}
}
}
運(yùn)行:
這里的方法叫做同步函數(shù),效果等同于同步代碼塊。
同步函數(shù)使用的鎖是this;
同步函數(shù) 和同步代碼塊的區(qū)別:
同步函數(shù)的鎖是固定的this,是同步代碼塊的簡寫形式;同步代碼塊的鎖是任意的對象。
開發(fā)時建議使用同步代碼塊。
class Ticket implements Runnable//extends Thread
{
private int num=100;
boolean flag=true;
public/* synchronized */void run(){
if(flag){
while(true){//這里不需要被同步,所以把if抽取到函數(shù)show里再調(diào)用
synchronized(this){//所以這里用this,控制與同步奇函數(shù)的線程用同一個鎖
if(num>0){
try{
Thread.sleep(10);
}catch(InterruptedException e){
}
System.out.println(Thread.currentThread().getName()+"...daima..."+num--);
}
}
}
}
else{
while(true){
this.show();//所以同步函數(shù)的鎖是this
}
}
}
public synchronized void show(){
if(num>0){
try{
Thread.sleep(10);
}catch(InterruptedException e){
}
System.out.println(Thread.currentThread().getName()+"...func..."+num--);
}
}
}
public class FuncLock {
public static void main(String[] args) {
Ticket t=new Ticket();
Thread t1=new Thread(t);
Thread t2=new Thread(t);
t1.start();
try{
Thread.sleep(10);//先暫停主線程
}catch(InterruptedException e){}
t.flag=false;
t2.start();
}
}
運(yùn)行沒有數(shù)據(jù)重復(fù)或者出現(xiàn)0,-1的情況:
靜態(tài)的同步函數(shù)使用的鎖是 該函數(shù)所屬字節(jié)碼文件對象 可以用getclass()獲取,也可以用 類名.class 獲取 。
將Ticket類改為:
class Ticket implements Runnable//extends Thread
{
private static int num=100;
boolean flag=true;
public/* synchronized */void run(){
if(flag){
while(true){//這里不需要被同步,所以把if抽取到函數(shù)show里再調(diào)用
synchronized(this.getClass()){//因為是靜態(tài),所以要換成獲取字節(jié)碼文件,也可以用(Ticket.class)
if(num>0){
try{
Thread.sleep(10);
}catch(InterruptedException e){
}
System.out.println(Thread.currentThread().getName()+"...daima..."+num--);
}
}
}
}
else{
while(true){
this.show();//所以同步函數(shù)的鎖是this
}
}
}
public static synchronized void show(){
if(num>0){
try{
Thread.sleep(10);
}catch(InterruptedException e){
}
System.out.println(Thread.currentThread().getName()+"...func..."+num--);
}
}
}