/**
* 例子:创建三个窗口卖票,总票数为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();
}
}