JAvaマルチスレッド例レコード

23470 ワード

Threadクラスを直接継承


【コード】
package thread;
public class MultiThread1 extends Thread{
public void run(){
for(int i=0; i<7; i++){
System.out.println("name:"+this.getName()+" i:"+ i+"   ");
}
}
public static void main(String[] args) {
MultiThread1 tA = new MultiThread1();
tA.setName("AA");
MultiThread1 tB = new MultiThread1();
tB.setName("BB");
tA.run();
tB.run();
}
}

【運転結果】
name:AA i:0   
name:AA i:1   
name:AA i:2   
name:AA i:3   
name:AA i:4   
name:AA i:5   
name:AA i:6   
name:BB i:0   
name:BB i:1   
name:BB i:2   
name:BB i:3   
name:BB i:4   
name:BB i:5   
name:BB i:6   
シーケンス実行は、呼び出し方法が間違っていることを示し、run()ではなくstart()を使用するべきである.
【コード】
package thread;
public class MultiThread1 extends Thread{
public void run(){
for(int i=0; i<7; i++){
System.out.println("name:"+this.getName()+" i:"+ i+"   ");
}
}
public static void main(String[] args) {
MultiThread1 tA = new MultiThread1();
tA.setName("AA");
MultiThread1 tB = new MultiThread1();
tB.setName("BB");
tA.start();
tB.start();
}
}

【運転結果】
name:BB i:0   
name:BB i:1   
name:AA i:0   
name:BB i:2   
name:AA i:1   
name:AA i:2   
name:AA i:3   
name:AA i:4   
name:AA i:5   
name:AA i:6   
name:BB i:3   
name:BB i:4   
name:BB i:5   
name:BB i:6   
実はstart()メソッドの内部でrun()メソッドが呼び出されています.スレッドを起動すると、システムリソースのサポートが必要になり、コードではマルチスレッドを初期化する環境として表現されます.次にrun()メソッドを呼び出して実行します.run()メソッドを単に呼び出すと,システムはマルチスレッドの環境を初期化せず,1つのスレッドで実行する.
start()のソースコード:
public synchronized void start() {
        /**
     * This method is not invoked for the main method thread or "system"
     * group threads created/set up by the VM. Any new functionality added
     * to this method in the future may have to also be added to the VM.
     *
     * A zero status value corresponds to state "NEW".
         */
        if (threadStatus != 0 || this != me)
            throw new IllegalThreadStateException();
        group.add(this);
        start0();
        if (stopBeforeStart) {
        stop0(throwableFromStop);
    }
}
private native void start0();

start0().そしてこの方法はnativeキーワードを用い,サブキーワードはローカルオペレーティングシステムを呼び出す関数を表す.マルチスレッドの実装にはローカルオペレーティングシステムのサポートが必要です.

Runnableインタフェースの実装


【コード】
package thread;
public class MyRunnable implements Runnable{
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public void run() {
// TODO Auto-generated method stub
for(int i=0; i<10; i++){
System.out.println("name:"+this.getName()+" i:"+ i+"   ");
}
}
public static void main(String[] args) {
MyRunnable mA = new MyRunnable();
mA.setName("AA");
MyRunnable mB = new MyRunnable();
mB.setName("BB");
Thread t1 = new Thread(mA);
Thread t2 = new Thread(mB);
t1.start();
t2.start();
}
}

【運転結果】
name:AA i:0   
name:AA i:1   
name:AA i:2   
name:BB i:0   
name:AA i:3   
name:BB i:1   
name:AA i:4   
name:BB i:2   
name:AA i:5   
name:BB i:3   
name:AA i:6   
name:BB i:4   
name:AA i:7   
name:BB i:5   
name:BB i:6   
name:BB i:7   
name:BB i:8   
name:BB i:9   
name:AA i:8   
name:AA i:9   
Threadも実装されたRunnableインタフェース
class Thread implements Runnable {
    //…
public void run() {
        if (target != null) {
             target.run();
        }
        }
}

ThreadとRunnableの違い:
クラスがThreadを継承する場合、リソース共有には適していません.しかしRunableインタフェースを実現すれば,容易にリソース共有を実現できる.

Threadを継承してリソースを共有できません


【コード】
package thread;
/**
 *  Thread , 
 * */
class Thread2 extends Thread {
private int count = 5;
    public void run() {
        for (int i = 0; i < 7; i++) {
            if (count > 0) {
                System.out.println("count= " + count--);
            }
        }
    }
    public static void main(String[] args) {
    Thread2 h1 = new Thread2();
    Thread2 h2 = new Thread2();
    Thread2 h3 = new Thread2();
        h1.start();
        h2.start();
        h3.start();
    }
 
    
}

【運転結果】
count= 5
count= 5
count= 5
count= 4
count= 4
count= 3
count= 4
count= 2
count= 3
count= 1
count= 3
count= 2
count= 2
count= 1
count= 1

Runnableがリソースを共有できるようにする


【コード】
package thread;
class Thread3 implements Runnable{
 
    private int ticket = 5;  //5 
    public void  run() {
        for (int i=0; i<=20; i++) {
            if (this.ticket > 0) {
                System.out.println(Thread.currentThread().getName()+ " "+this.ticket--);
            }
        }
    }
    public static void main(String[] args) {
    Thread3 my = new Thread3();
        new Thread(my, "1 ").start();
        new Thread(my, "2 ").start();
        new Thread(my, "3 ").start();
}
}

【運転結果】
1番窓口でチケットを販売しております5
3番窓口で切符を売っています.
2番窓口で切符を売っています.
3番窓口で切符を売っています.
1番窓口でチケットを販売しております2
まとめてみましょう.
Runnableインタフェースを実装することは、Threadクラスを継承することよりも優れています.
1):同じプログラムコードを複数適用したスレッドが同じリソースを処理する
2):javaでの単一継承の制限を回避できます.
3):プログラムの堅牢性を高め、コードは複数のスレッドで共有でき、コードとデータは独立している.
注意:mainメソッドもスレッドです.Javaではスレッドが同時に起動しているので、いつ、どれが先に実行されるかは、誰が先にCPUのリソースを得るかを完全に見ています.
 
Javaでは、プログラムが実行されるたびに少なくとも2つのスレッドが起動されます.1つはmainスレッド、1つはゴミ収集スレッドです.Javaコマンドを使用してクラスを実行するたびに、実際にはJVMが起動されるため、各jVM実習はオペレーティングシステムでプロセスを開始します.
isAliveスレッドが起動しているかどうかを判断
【コード】
MultiThread1 tA = new MultiThread1();
tA.setName("AA");
System.out.println("  isAlive :"+ tA.isAlive());
tA.start();
System.out.println("  isAlive :"+ tA.isAlive());

【運転結果】
起動前isAlive:false
起動後isAlive:true

スレッドの強制実行


【コード】
package thread;
public class MyRunnable implements Runnable{
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public void run() {
// TODO Auto-generated method stub
for(int i=0; i<6; i++){
System.out.println("name:"+this.getName()+" i:"+ i+"   ");
}
}
public static void main(String[] args) throws InterruptedException {
MyRunnable mA = new MyRunnable();
mA.setName("AA");
Thread t1 = new Thread(mA);
t1.start();
for(int i = 1; i<50; i++){
System.out.println("main  : "+i);
if(i == 3){
t1.join();
}
}
}
}

【運転結果】
mainスレッド:1
mainスレッド:2
mainスレッド:3
name:AA i:0   
name:AA i:1   
name:AA i:2   
name:AA i:3   
name:AA i:4   
name:AA i:5   
mainスレッド:4
mainスレッド:5
mainスレッド:6
mainスレッド:7
...

スレッドのスリープ


【コード】
package thread;
public class MyRunnable implements Runnable{
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public void run() {
// TODO Auto-generated method stub
for(int i=0; i<3; i++){
System.out.println("name:"+this.getName()+" i:"+ i+"   ");
if(i == 1){
try {
Thread.currentThread().sleep(2000);
System.out.println(" 2 --------");
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
public static void main(String[] args) throws InterruptedException {
MyRunnable mA = new MyRunnable();
mA.setName("AA");
Thread t1 = new Thread(mA);
t1.start();
}
}

【運転結果】
name:AA i:0   
name:AA i:1   
睡眠2秒------
name:AA i:2  
スリープ2秒印刷まで2秒待ち

スレッド割込み


【コード】
package thread;
public class MyRunnable implements Runnable{
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public void run() {
// TODO Auto-generated method stub
System.out.println(" ");
try {
Thread.currentThread().sleep(10000);
System.out.println(" ");
} catch (InterruptedException e) {
// TODO Auto-generated catch block
System.out.println(" !");
e.printStackTrace();
}
}
public static void main(String[] args) throws InterruptedException {
MyRunnable mA = new MyRunnable();
mA.setName("AA");
Thread t1 = new Thread(mA);
t1.start();
Thread.currentThread().sleep(2000);
t1.interrupt();//2 
}
}

【運転結果】
スレッド開始
スレッドのスリープが中断されました!
java.lang.InterruptedException: sleep interrupted
at java.lang.Thread.sleep(Native Method)
at thread.MyRunnable.run(MyRunnable.java:17)
at java.lang.Thread.run(Thread.java:745)
Javaプログラムでは、フロントにスレッドが実行されている限り、javaプログラムプロセス全体が消えないので、javaプロセスが時間が経ってもバックグラウンドスレッドを設定できます.
【コード】
package thread;
public class MyRunnable implements Runnable{
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public void run() {
while(true){
System.out.println(Thread.currentThread().getName() + " ");
}
}
public static void main(String[] args) throws InterruptedException {
MyRunnable mA = new MyRunnable();
mA.setName("AA");
Thread t1 = new Thread(mA);
t1.setName("AA");
t1.start();
}
}

【運転結果】
AA運転中
AA運転中
AA運転中
AA運転中
AA運転中
...
デッドサイクル、ずっと印刷

スレッドの優先度


【コード】
package thread;
public class MyRunnable implements Runnable{
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
MyRunnable(String name){
this.name = name;
}
@Override
public void run() {
        for(int i=0;i<5;++i){
            System.out.println(Thread.currentThread().getName()+" "+i);
        }
}
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(new MyRunnable("Thread1"));
t1.setName("AA");
Thread t2 = new Thread(new MyRunnable("Thread2"));
t2.setName("BB");
Thread t3 = new Thread(new MyRunnable("Thread3"));
t3.setName("CC");
t2.setPriority(1);
t2.setPriority(10);
t3.setPriority(5);
t1.start();
t2.start();
t3.start();
}
}

【運転結果】
BB運転0
BB運転1
BB運転2
BB運転3
BB運転4
AA運転0
AA運転1
AA運転2
CC運転0
CC運転1
CC運転2
CC運転3
CC運転4
AA運転3
AA運転4
優先度が高いほど先に実行すると勘違いしないでください.誰が先に実行するかは、誰が先に行ったCPUのリソースによって決まります.
 
また、メインスレッドの優先度は5である.

スレッドのギフトyield()


この方法はsleep()と同様であるが,ユーザがどのくらい一時停止するかを指定することはできず,yield()メソッドは同優先度のスレッドに実行の機会を与えるしかない.
【コード】
package thread;
public class MyRunnable implements Runnable{
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
MyRunnable(String name){
this.name = name;
}
@Override
public void run() {
        for(int i=0;i<5;++i){
            System.out.println(Thread.currentThread().getName()+" "+i);
            if(i == 2 && Thread.currentThread().getName().equals("BB")){
            Thread.currentThread().yield();
            System.out.println(" ");
            }
        }
}
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(new MyRunnable("Thread1"));
t1.setName("AA");
Thread t2 = new Thread(new MyRunnable("Thread2"));
t2.setName("BB");
t2.setPriority(6);
t1.setPriority(6);
t1.start();
t2.start();
}
}

【運転結果】
BB運転0
BB運転1
BB運転2
AA運転0
スレッド礼譲
BB運転3
AA運転1
AA運転2
AA運転3
AA運転4
BB運転4
同期とデッドロック
チケット販売システムについては、次のような問題があります.
【コード】
package thread;
public class MyRunnable implements Runnable{
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
MyRunnable(String name){
this.name = name;
}
private int count = 5;
@Override
public void run() {
        for(int i=1;i<10;i++){
        if(count > 0){
        try {
        Thread.sleep(500);
        } catch (InterruptedException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
        }
        System.out.println(" : "+ count--);
        }
        }
}
public static void main(String[] args) throws InterruptedException {
MyRunnable m = new MyRunnable("Thread1");
System.out.println(m.count);
Thread t1 = new Thread(m);
t1.setName("AA");
Thread t2 = new Thread(m);
t2.setName("BB");
Thread t3 = new Thread(m);
t3.setName("CC");
t1.start();
t2.start();
t3.start();
}
}

【運転結果】
5
残り票数:5
残り票数:5
残り票数:5
残りの票数:4
残り票数:3
残り票数:2
残り票数:1
残り票数:0
残り票:-1
ここに-1が現れました.明らかにこれは間違っています.、票数はマイナスにならないはずです.
この問題を解決するには、同期を使用する必要があります.同期とは、統合期間中に1つのスレッドだけが実行されることであり、
他のスレッドは、このスレッドが終了してから実行を続行する必要があります.
【スレッド同期による問題解決】
同期を採用すれば、同期コードブロックと同期方法の2つを使用して完了することができます.
 

【同期コードブロック】:


構文の形式:
synchronized(同期オブジェクト){
//同期が必要なコード
}
ただし、現在のオブジェクトthisを同期オブジェクトとするのが一般的です.
【コード】
package thread;
public class MyRunnable implements Runnable{
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
MyRunnable(String name){
this.name = name;
}
private int count = 5;
@Override
public void run() {
        for(int i=1;i<10;i++){
        synchronized (this) {
        if(count > 0){
        try {
        Thread.sleep(500);
        } catch (InterruptedException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
        }
        System.out.println(" : "+ count--);
        }
}
        }
}
public static void main(String[] args) throws InterruptedException {
MyRunnable m = new MyRunnable("Thread1");
System.out.println(m.count);
Thread t1 = new Thread(m);
t1.setName("AA");
Thread t2 = new Thread(m);
t2.setName("BB");
Thread t3 = new Thread(m);
t3.setName("CC");
t1.start();
t2.start();
t3.start();
}
}

【運転結果】
5
残り票数:5
残りの票数:4
残り票数:3
残り票数:2
残り票数:1
構文フォーマットsynchronizedメソッドはタイプメソッド名(パラメータリスト)を返します{
//その他コード
}
【コード】
package thread;
public class MyRunnable implements Runnable{
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
MyRunnable(String name){
this.name = name;
}
private int count = 5;
@Override
public void run() {
        for(int i=1;i<10;i++){
        sale();
        }
}
public synchronized void sale(){
if(count > 0){
try {
Thread.sleep(500);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(" : "+ count--);
}
}
public static void main(String[] args) throws InterruptedException {
MyRunnable m = new MyRunnable("Thread1");
System.out.println(m.count);
Thread t1 = new Thread(m);
t1.setName("AA");
Thread t2 = new Thread(m);
t2.setName("BB");
Thread t3 = new Thread(m);
t3.setName("CC");
t1.start();
t2.start();
t3.start();
}
}

【運転結果】
5
残り票数:5
残りの票数:4
残り票数:3
残り票数:2
残り票数:1

複数のスレッドが1つのリソースを共有する場合は同期が必要ですが、同期が多すぎるとデッドロックになる可能性があります。生産者と消費者の問題。


ここでは古典的な生産者と消費者の問題を列挙する.
【コード】
情報クラス
package lock;
public class Info {
private String name = "wangting";
private int age = 20;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}

生産者
package lock;
class Producer implements Runnable{
    private Info info=null;
    Producer(Info info){
        this.info=info;
    }
     
    public void run(){
        boolean flag=false;
        for(int i=0;i<25;++i){
            if(flag){
                this.info.setName("wangting");
                try{
                    Thread.sleep(100);
                }catch (Exception e) {
                    e.printStackTrace();
                }
                this.info.setAge(20);
                flag=false;
            }else{
                this.info.setName(" ");
                try{
                    Thread.sleep(100);
                }catch (Exception e) {
                    e.printStackTrace();
                }
                this.info.setAge(10000);
                flag=true;
            }
        }
    }
}

消費者
package lock;
class Consumer implements Runnable{
    private Info info=null;
    public Consumer(Info info){
        this.info=info;
    }
     
    public void run(){
        for(int i=0;i<25;++i){
            try{
                Thread.sleep(100);
            }catch (Exception e) {
                e.printStackTrace();
            }
            System.out.println(this.info.getName()+"<---->"+this.info.getAge());
        }
    }
}

テストコード
package lock;
public class Test {
    public static void main(String[] args) {
        Info info=new Info();
        Producer pro=new Producer(info);
        Consumer con=new Consumer(info);
        new Thread(pro).start();
        new Thread(con).start();
    }
}

【運転結果】
wangting<---->10000
wangting<---->20
wangting<---->10000
斉天大聖
wangting<---->10000
斉天大聖
wangting<---->10000
斉天大聖
wangting<---->10000
斉天大聖
wangting<---->10000
斉天大聖
wangting<---->10000
斉天大聖
wangting<---->10000
斉天大聖
wangting<---->10000
斉天大聖
wangting<---->10000
斉天大聖
wangting<---->10000
斉天大聖
wangting<---->10000
斉天大聖
斉天大聖<---->10000
名前と年齢が対応していないことが結果からわかります.
斉天大聖
斉天大聖<---->10000

2つの解決策


1)同期の追加
2)待機と起動を加える

1同期の追加


【コード】
情報クラス
package lock;
public class Info {
private String name = "wangting";
private int age = 20;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public synchronized void set(String name, int age){
        this.name=name;
        try{
            Thread.sleep(100);
        }catch (Exception e) {
            e.printStackTrace();
        }
        this.age=age;
    }
     
    public synchronized void get(){
        try{
            Thread.sleep(100);
        }catch (Exception e) {
            e.printStackTrace();
        }
        System.out.println(this.getName()+"<===>"+this.getAge());
    }
    
}

生産者
package lock;
class Producer implements Runnable{
    private Info info=null;
    Producer(Info info){
        this.info=info;
    }
     
    public void run(){
        boolean flag=false;
        for(int i=0;i<25;++i){
            if(flag){
                try{
                    Thread.sleep(100);
                }catch (Exception e) {
                    e.printStackTrace();
                }
                this.info.set("wangting",20);
                flag=false;
            }else{
                try{
                    Thread.sleep(100);
                }catch (Exception e) {
                    e.printStackTrace();
                }
                this.info.set(" ",10000);
                flag=true;
            }
        }
    }
}

消費者
package lock;
class Consumer implements Runnable{
    private Info info=null;
    public Consumer(Info info){
        this.info=info;
    }
     
    public void run(){
        for(int i=0;i<25;++i){
            try{
                Thread.sleep(100);
            }catch (Exception e) {
                e.printStackTrace();
            }
            this.info.get();
        }
    }
}

テストコード
package lock;
public class Test {
    public static void main(String[] args) {
        Info info=new Info();
        Producer pro=new Producer(info);
        Consumer con=new Consumer(info);
        new Thread(pro).start();
        new Thread(con).start();
    }
}

【運転結果】
斉天大聖<==>10000
斉天大聖<==>10000
wangting<===>20
wangting<===>20
斉天大聖<==>10000
wangting<===>20
斉天大聖<==>10000
wangting<===>20
斉天大聖<==>10000
wangting<===>20
斉天大聖<==>10000
wangting<===>20
斉天大聖<==>10000
wangting<===>20
斉天大聖<==>10000
wangting<===>20
斉天大聖<==>10000
wangting<===>20
斉天大聖<==>10000
wangting<===>20
斉天大聖<==>10000
wangting<===>20
斉天大聖<==>10000
斉天大聖<==>10000
wangting<===>20
以上、錯乱に対応した問題が解決しました.

Object類は重複問題の解決を手伝う


しかし、重複読み取りの問題もあり、重複オーバーライドの問題もあるに違いない.この問題を解決するには、Objectクラスのヘルプが必要です.
を選択すると、待機および起動操作を使用できます.
上記の機能を完了するには、Infoクラスを変更し、フラグビットを追加し、フラグビットを判断することで待機と起動の操作を完了する必要があります.コードは次のとおりです.
【コード】
package lock;
public class Info {
private String name = "wangting";
private int age = 20;
private boolean flag = false;//  
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public boolean isFlag() {
return flag;
}
public void setFlag(boolean flag) {
this.flag = flag;
}
public synchronized void set(String name, int age){
if(!flag){
try {
super.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
        this.name=name;
        try{
            Thread.sleep(100);
        }catch (Exception e) {
            e.printStackTrace();
        }
        this.age=age;
        flag=false;
        super.notify();
    }
     
    public synchronized void get(){
    if(flag){
    try {
super.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
    }
        try{
            Thread.sleep(100);
        }catch (Exception e) {
            e.printStackTrace();
        }
        System.out.println(this.getName()+"<===>"+this.getAge());
        flag=true;
        super.notify();
    }
    
}

【運転結果】
wangting<===>20
斉天大聖<==>10000
wangting<===>20
斉天大聖<==>10000
wangting<===>20
斉天大聖<==>10000
wangting<===>20
斉天大聖<==>10000
wangting<===>20
斉天大聖<==>10000
wangting<===>20
斉天大聖<==>10000
wangting<===>20
斉天大聖<==>10000
wangting<===>20
斉天大聖<==>10000
wangting<===>20
斉天大聖<==>10000
wangting<===>20
斉天大聖<==>10000
wangting<===>20
斉天大聖<==>10000
wangting<===>20
斉天大聖<==>10000
wangting<===>20