目录
总体框架:
/** * 题目:三个窗口售票,总票数100张,售完为止 * * 出现线程安全解决方式: * 方式一: * 1、同步代码块; * synchronized(同步代码块){ * //需要被同步的代码 * } * 说明:1、操作共享数据的代码,即为需要被同步的代码 * 2、共享数据:多个线程共同操作的变量,比如:ticket就是共享数据。 * 3、同步监视器,俗称:锁。任何一个类都可以充当一把锁。 * 要求:多个线程必须用同意把锁 * * 2、this * synchronized(this){ //this表示当前对象,且当前对象只有一个 * * } * 3、当前类.class充当,类只会加载一次 * synchronized(当前类.class){ * * } * * 方式二:同步方法 * 如果操作共享数据的代码完整的声明在一个方法中国,我们不妨将此方法声明同步的 * 同步代码块总结: * 1、同步方法仍涉及到同步监控器,只是不愿要我们显示声明 * 2、非静态的同步方式,同步监控器是:this * 静态的同步方式,同步监控器是当前类本身 * * *使用同步机制将单例模式中的懒汉式更改为线程安全的 * * * 解决线程问题的方式三:Lock锁-----JDK5.0新增 * 1、实例化lock * 2、调用锁定方法lock() * 3、调用解锁方法:unlock() */
/*
* 出现线程安全解决方式:
* 方式一:
* 1、同步代码块;
* synchronized(同步代码块){
* //需要被同步的代码
* }
*/
class window1 implements Runnable{
private int ticket = 100;
Object obj = new Object();
@Override
public void run() {
while(true){
// 这里也可以使用this,需要把obj的创建对象删除,在把synchronized()括号里替换成this
synchronized(obj) {
if (ticket > 0) {
// 出现线程安全
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "卖票,剩余票数:" + ticket);
ticket--;
} else {
break;
}
}
}
}
}
public class Test1 {
public static void main(String[] args) {
window1 wd = new window1();
Thread t1=new Thread(wd);
Thread t2=new Thread(wd);
Thread t3=new Thread(wd);
t1.setName("窗口一");
t2.setName("窗口二");
t3.setName("窗口三");
t1.start();
t2.start();
t3.start();
}
}
/**
* 方式二:
* 使用同步方法解决实现Runnable接口的线程安全问题
* 代码的执行放在另一个方法中,声明的方式的返回值是synchronized
*
*/
class window2 implements Runnable{
private int ticket = 100;
@Override
public void run() {
while(true){
show();
}
}
private synchronized void show(){
// 或者使用同步代码块:synchronized (this) {
if (ticket > 0) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "卖票,剩余票数:" + ticket);
ticket--;
}
}
}
public class Test2 {
public static void main(String[] args) {
window2 wd = new window2();
Thread t1=new Thread(wd);
Thread t2=new Thread(wd);
Thread t3=new Thread(wd);
t1.setName("窗口一");
t2.setName("窗口二");
t3.setName("窗口三");
t1.start();
t2.start();
t3.start();
}
}
同步方法改进:
使用同步机制将单例模式中的懒汉式更改为线程安全的
class Bank {
private Bank() {
}
private static Bank instance = null;
public static Bank getInstance() {
// 方式一:效率差
// synchronized (Bank.class) {
// if (instance == null) {
// instance = new Bank();
// }
// return instance;
// }
// 方式二:效率高一点
if (instance == null) {
//Bank.class:是代表当前类
synchronized (Bank.class) {
if (instance == null) {
instance = new Bank();
}
return instance;
}
}
return null;
}
}
解决线程问题的方式三:Lock锁-----JDK5.0新增
class Window4 implements Runnable{
private int ticket =100;
// 1、实例化lock
private ReentrantLock lock=new ReentrantLock(true);
@Override
public void run() {
while(true){
try {
// 2、调用锁定方法lock();
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 {
// 3、调用解锁方法:unlock()
lock.unlock();
}
}
}
}
public class Test5 {
public static void main(String[] args) {
Window4 window=new Window4();
Thread t1=new Thread(window);
Thread t2=new Thread(window);
Thread t3=new Thread(window);
t1.setName("窗口一");
t2.setName("窗口二");
t3.setName("窗口三");
t1.start();
t2.start();
t3.start();
}
}
方式三修改
**
* 创建线程的方式三:实现collable接口的方式。----JDK 5.0新增
* callable接口的方式创建多线程比Runnable接口创建多线程方式强大?
* 1、call()可以有返回值
* 2、call()可以抛出异常,被外面的操作捕获,获取异常的信息
* 3、callable是支持泛型的
* Description:
* @author: ----千里之行,始于足下----
* date: 2022/8/15 15:12
*/
//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 {
// 6、获取callable中call方法的返回值
// get()返回值即为FutureTask构造器参数Callable实现类重写的call()的返回值。
Object sum = futureTask.get();
System.out.println(sum);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}