JAVA---多執行緒1
阿新 • • 發佈:2022-03-17
/** * 例子:建立三個視窗賣票,總票數為100張,使用實現Runnable介面的方式 * * 解決執行緒安全問題: * 方式一:同步程式碼塊 * * synchronized(同步監視器){ * //需要被同步的程式碼 * * } * * 說明:1.操作共享資料的程式碼,即為需要被同步的程式碼。 * 2.共享資料:多個執行緒共同操作的變數。 * 3.同步監視器,也叫鎖。任何一個類的物件,都可以充當鎖。 * 要求:多個執行緒必須要公用同一把鎖。 * * 在使用Runnable介面建立多執行緒的方式中,可以考慮用this充當同步監視器 * *5.同步的方式,解決了執行緒的安全問題--好處 * 操作同步程式碼時,只能有一個執行緒參與,其他執行緒等待。相當於是一個單執行緒的過程,效率低----侷限性 * * * @author nkc * @create 2022-03-12 12:56 */ class Window1 implements Runnable{ private int ticket=100; @Override public void run() { while(true){ synchronized (this){ if(ticket>0){ try { Thread.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+":賣票,票號為:"+ticket); ticket--; }else{ break; } } } } } public class WindowTest1 { public static void main(String[] args) { Window1 w=new Window1(); Thread t1=new Thread(w); Thread t2=new Thread(w); Thread t3=new Thread(w); t1.setName("視窗1"); t2.setName("視窗2"); t3.setName("視窗3"); t1.start(); t2.start(); t3.start(); } }
/** * * 使用同步程式碼塊解決繼承Thread類的方式的執行緒安全問題。 * * 說明:在繼承Thread類建立多執行緒的方式中,慎用this充當同步監視器,考慮使用當前類充當同步監視器 * * * @create 2022-03-12 14:53 */ class Window2 extends Thread{ private static int ticket=100; @Override public void run() { while (true){ synchronized (Window2.class){ if(ticket>0){ try { Thread.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(getName()+":賣票,票號為:"+ticket); ticket--; }else{ break; } } } } } public class WindowTest2 { public static void main(String[] args) { Window2 t1=new Window2(); Window2 t2=new Window2(); Window2 t3=new Window2(); t1.setName("視窗1"); t2.setName("視窗2"); t3.setName("視窗3"); t1.start(); t2.start(); t3.start(); } }
/** * * 使用同步方法解決實現Runnable介面的執行緒安全問題 * * * 關於同步方法的總結: * 1.同步方法仍然涉及到同步監視器,只是不需要我們顯式的宣告 * 2.非靜態的同步方法,同步監視器是:this * 靜態的同步方法,同步監視器是:當前類本身 * * @create 2022-03-12 15:56 */ class Window3 implements Runnable{ private int ticket=100; @Override public void run() { while (true){ show(); } } private synchronized void show(){//同步監視器:this if (ticket>0) { System.out.println(Thread.currentThread().getName() + ":賣票,票號為" + ticket); ticket--; } } } public class WindowTest3 { public static void main(String[] args) { Window3 w=new Window3(); Thread t1=new Thread(w); Thread t2=new Thread(w); Thread t3=new Thread(w); t1.setName("視窗1"); t2.setName("視窗2"); t3.setName("視窗3"); t1.start(); t2.start(); t3.start(); } }
/**
*
* 使用同步方法處理繼承Thread類的方式中的執行緒安全問題
*
*
* @create 2022-03-12 16:14
*/
class Window4 extends Thread{
private static int ticket=100;
@Override
public void run() {
while (true){
show();
}
}
private static synchronized void show(){//同步監視器:Window4.class
if(ticket>0){
System.out.println(Thread.currentThread().getName()+":賣票,票號為:"+ticket);
ticket--;
}
}
}
public class WindowTest4 {
public static void main(String[] args) {
Window4 t1=new Window4();
Window4 t2=new Window4();
Window4 t3=new Window4();
t1.setName("視窗1");
t2.setName("視窗2");
t3.setName("視窗3");
t1.start();
t2.start();
t3.start();
}
}
package com.atguigu.java1;
/**
* 使用同步機制將單例模式中的懶漢式改寫為執行緒安全的
*
* @author shkstart
* @create 2019-02-15 下午 2:50
*/
public class BankTest {
}
class Bank{
private Bank(){}
private static Bank instance = null;
// public static synchronized Bank getInstance()
public static Bank getInstance(){
//方式一:效率稍差
// synchronized (Bank.class) {
// if(instance == null){
//
// instance = new Bank();
// }
// return instance;
// }
//方式二:效率更高
if(instance == null){
synchronized (Bank.class) {
if(instance == null){
instance = new Bank();
}
}
}
return instance;
}
}
package java1;
/**
*
* 執行緒的死鎖
* 1.死鎖的理解:不同的執行緒分別佔用對方需要的同步資源不放棄。
* 都在等待對方放棄自己需要的同步資源,就形成了執行緒的死鎖。
*
* 2.說明
* 1)出現死鎖後,不會出現異常,不會出現提示,只是所有的執行緒都處於阻塞狀態,無法繼續。
* 2)使用同步時,要避免出現死鎖。
*
*
*
* @create 2022-03-15 11:13
*/
public class ThreadTest {
public static void main(String[] args) {
StringBuffer s1=new StringBuffer();
StringBuffer s2=new StringBuffer();
new Thread(){
@Override
public void run() {
synchronized(s1){
s1.append("a");
s2.append("1");
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized(s2){
s1.append("b");
s2.append("2");
System.out.println(s1);
System.out.println(s2);
}
}
}
}.start();
new Thread(new Runnable() {
@Override
public void run() {
synchronized (s2){
s1.append("c");
s2.append("3");
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (s1){
s1.append("d");
s2.append("4");
System.out.println(s1);
System.out.println(s2);
}
}
}
}).start();
}
}
package java1;
import java.util.concurrent.locks.ReentrantLock;
/**
*
* 解決執行緒安全問題的方式三:Lock鎖-----JDK 5.0新增
*
* 1.synchronized 與 Lock的異同?
* 相同:二者都可以解決執行緒安全問題
* 不同:synchronized機制在執行完相應的同步程式碼以後,自動的釋放同步監視器
* lock需要手動的啟動同步(Lock),同時結束同步也需要手動實現(unlock)
*
*
* @create 2022-03-15 12:29
*/
class Window implements Runnable{
private int ticket=100;
//1.例項化ReentrantLock
private ReentrantLock lock=new ReentrantLock();
@Override
public void run() {
while (true){
try{
lock.lock();
if(ticket>0){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+":售票,票號為:"+ticket);
ticket--;
}else{
break;
}
}finally {
lock.unlock();
}
}
}
}
public class Locktest {
public static void main(String[] args) {
Window w=new Window();
Thread t1=new Thread(w);
Thread t2=new Thread(w);
Thread t3=new Thread(w);
t1.setName("視窗1");
t2.setName("視窗2");
t3.setName("視窗3");
t1.start();
t2.start();
t3.start();
}
}
package exer;
/**
* 銀行有一個賬戶。
有兩個儲戶分別向同一個賬戶存3000元,每次存1000,存3次。每次存完列印賬戶餘額。
* @create 2022-03-15 19:32
*/
class Account{
private double balance;
public Account(double balance){
this.balance=balance;
}
//存錢
public synchronized void deposit(double amt){
if(amt>0){
balance+=amt;
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+":存錢成功,餘額為:"+balance);
}
}
}
class Customer extends Thread{
private Account acct;
public Customer(Account acct){
this.acct=acct;
}
@Override
public void run() {
for (int i = 0; i < 3; i++) {
acct.deposit(1000);
}
}
}
public class AccountTest {
public static void main(String[] args) {
Account acct=new Account(0);
Customer c1=new Customer(acct);
Customer c2=new Customer(acct);
c1.setName("甲");
c2.setName("乙");
c1.start();
c2.start();
}
}
package java2;
/** 執行緒通訊的例子:使用兩個執行緒列印 1-100。執行緒1, 執行緒2 交替列印
*
* 涉及到的三個方法:
* wait():一旦執行此方法,當前執行緒就會進入阻塞狀態,並釋放同步監視器。
* notify():一旦執行此方法,就會喚醒被wait的一個執行緒。如果有多個執行緒被wait,
* 就喚醒優先順序高的那個。
* notifyAll():一旦執行此方法,就會喚醒所有被wait的執行緒。
*
* 說明:
* 1.wait(),notify(),notifyAll()三個方法必須使用在同步程式碼塊或同步方法中。
* 2.wait(),notify(),notifyAll()三個方法的呼叫者必須是同步程式碼塊或同步方法中的同步監視器。
* 否則,會出現IllegalMonitorStateException異常。
* 3.wait(),notify(),notifyAll()三個方法是定義在java.lang.Object類中。
*
*
* sleep()和wait()的異同?
* 1.相同點:一旦執行此方法,都可以使得當前的執行緒進入阻塞狀態。
* 2.不同點:
* 1)兩個方法宣告的位置不同:Thread類中宣告sleep(),Object類中宣告wait().
* 2)呼叫的要求不同:sleep()可以在任何需要的場景呼叫。wait()必須使用在同步程式碼塊或同步方法中。
* 3)關於是否釋放同步監視器:如果兩個方法都使用在同步程式碼塊或同步方法中,
* sleep()不會釋放鎖,wait()會釋放鎖。
*
* @create 2022-03-15 20:23
*/
class Number implements Runnable{
private int number=1;
private Object obj=new Object();
@Override
public void run() {
while (true){
synchronized (obj){
//喚醒
obj.notify();
if(number<=100){
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+":"+number);
number++;
try {
//呼叫wait的程序進入阻塞狀態。
obj.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}else{
break;
}
}
}
}
}
public class CommunciationTest {
public static void main(String[] args) {
Number number=new Number();
Thread t1=new Thread(number);
Thread t2=new Thread(number);
t1.setName("執行緒1");
t2.setName("執行緒2");
t1.start();
t2.start();
}
}
package java2;
/**
*
* 生產者消費者問題
*
* 生產者(Productor)將產品交給店員(Clerk),而消費者(Customer)從店員處取走產品,
* 店員一次只能持有固定數量的產品(比如:20),如果生產者試圖生產更多的產品,店員
* 會叫生產者停一下,如果店中有空位放產品了再通知生產者繼續生產;如果店中沒有產品
* 了,店員會告訴消費者等一下,如果店中有產品了再通知消費者來取走產品。
*
*
*
* @create 2022-03-17 11:25
*/
class Clerk{
private int productCount=0;
public synchronized void produceProduct(){
if(productCount<20){
productCount++;
System.out.println(Thread.currentThread().getName()+":開始生產第"+productCount+"個產品");
notify();
}else{
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public synchronized void consumeProduct(){
if(productCount>0){
System.out.println(Thread.currentThread().getName()+":開始消費第"+productCount+"個產品");
productCount--;
notify();
}else{
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
//生產者
class Producer extends Thread{
private Clerk clerk;
public Producer(Clerk clerk){
this.clerk=clerk;
}
@Override
public void run() {
System.out.println(getName()+":開始生產產品。。。。");
while (true){
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
clerk.produceProduct();
}
}
}
//消費者
class Consumer extends Thread{
private Clerk clerk;
public Consumer(Clerk clerk){
this.clerk=clerk;
}
@Override
public void run() {
System.out.println(getName()+"開始消費產品。。。");
while (true){
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
clerk.consumeProduct();
}
}
}
public class ProductTest {
public static void main(String[] args) {
Clerk clerk=new Clerk();
Producer p1=new Producer(clerk);
p1.setName("生產者1");
Consumer c1=new Consumer(clerk);
c1.setName("消費者1");
Consumer c2=new Consumer(clerk);
c2.setName("消費者2");
p1.start();
c1.start();
c2.start();
}
}
package java2;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
/**
* 建立執行緒的方式三:實現Callable介面。----JDK5.0新增
*
* 如何理解Callable介面的方式建立多執行緒比實現Runnable介面建立多執行緒方式強大?
* 1.call()是有返回值的
* 2.call()可以丟擲異常,被外面的操作捕獲,獲取異常的資訊。
* 3.Callable()是支援泛型的。
*
* @create 2022-03-17 16:28
*/
//1.建立一個實現Callable的實現類
class NumThread implements Callable{
//2.實現call()方法,將此執行緒需要執行的操作宣告在call()中。
@Override
public Object call() throws Exception {
int sum=0;
for (int i = 1; i < 100; i++) {
if(i%2==0){
System.out.println(i);
sum+=i;
}
}
return sum;
}
}
public class ThreadNew {
public static void main(String[] args) {
//3.建立Callable介面實現類的物件。
NumThread numThread=new NumThread();
//4.將此Callable介面實現類的物件傳遞到FutureTask的構造器中,建立FutureTask的物件
FutureTask futureTask=new FutureTask(numThread);
//5.將FutureTask的物件作為引數傳遞到Thread類的構造器中,建立Thread物件,並呼叫start()
new Thread(futureTask).start();
try {
Object sum = futureTask.get();
System.out.println("總和為:"+sum);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
package java2;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
/**
* 建立執行緒的方式三:實現Callable介面。----JDK5.0新增
*
* 如何理解Callable介面的方式建立多執行緒比實現Runnable介面建立多執行緒方式強大?
* 1.call()是有返回值的
* 2.call()可以丟擲異常,被外面的操作捕獲,獲取異常的資訊。
* 3.Callable()是支援泛型的。
*
* @create 2022-03-17 16:28
*/
//1.建立一個實現Callable的實現類
class NumThread implements Callable{
//2.實現call()方法,將此執行緒需要執行的操作宣告在call()中。
@Override
public Object call() throws Exception {
int sum=0;
for (int i = 1; i < 100; i++) {
if(i%2==0){
System.out.println(i);
sum+=i;
}
}
return sum;
}
}
public class ThreadNew {
public static void main(String[] args) {
//3.建立Callable介面實現類的物件。
NumThread numThread=new NumThread();
//4.將此Callable介面實現類的物件傳遞到FutureTask的構造器中,建立FutureTask的物件
FutureTask futureTask=new FutureTask(numThread);
//5.將FutureTask的物件作為引數傳遞到Thread類的構造器中,建立Thread物件,並呼叫start()
new Thread(futureTask).start();
try {
//獲取Callable中call方法的返回值
Object sum = futureTask.get();
System.out.println("總和為:"+sum);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
package java2;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;
/**
* 建立執行緒的方式四:使用執行緒池
*
* 好處:
* 1.提高響應速度(減少了建立新執行緒的時間)
* 2.降低資源消耗(重複利用執行緒池中執行緒,不需要每次都建立)
* 3.便於執行緒管理
* corePoolSize:核心池的大小
* maximumPoolSize:最大執行緒數
* keepAliveTime:執行緒沒有任務時最多保持多長時間後會終止。
*
*
* @create 2022-03-17 17:45
*/
class NumberThread implements Runnable{
@Override
public void run() {
for (int i = 0; i <= 100; i++) {
if(i%2==0){
System.out.println(Thread.currentThread().getName()+":"+i);
}
}
}
}
class NumberThread1 implements Runnable{
@Override
public void run() {
for (int i = 0; i <= 100; i++) {
if(i%2!=0){
System.out.println(Thread.currentThread().getName()+":"+i);
}
}
}
}
public class ThreadPool {
public static void main(String[] args) {
//1.提供指定執行緒數量的執行緒池
ExecutorService service= Executors.newFixedThreadPool(10);
ThreadPoolExecutor service1=(ThreadPoolExecutor) service;
//設定執行緒池的屬性
// System.out.println(service.getClass());
// service1.setCorePoolSize(15);
// service1.setKeepAliveTime(100);
//2.執行指定的執行緒的操作,需要提供實現Runnable介面或Callable介面實現類的物件‘
service.execute(new NumberThread());//適用於Runnable
service.execute(new NumberThread1());//適用於Runnable
//3.關閉連線池
service.shutdown();
}
}