Java多线程使用同步机制处理线程安全的理解
** ** Java中多线程的创建这里只说两种。
1.继承于Thread类
①即创建一个Thread的子类
②重写Thread类中的run()方法,方法体中写线程执行的操作
③通过子类的对象调用Thread类中的start()方法,通过start()方法启动线程,
由于通过动态绑定的机制,最终调用该线程的run()方法,即子类重写的。
//创建一个继承Thread类的子类
class ThreadTest extends Thread{
//重写父类中的run()方法
@Override
public void run() {
for (int i = 0; i < 20; i++) {
System.out.println(currentThread().getName() + ":" + i);
}
}
}
public class Textkang {
public static void main(String[] args) {
//创建子类对象
ThreadTest threadTest = new ThreadTest();
ThreadTest threadTest1 = new ThreadTest();
//设置线程名
threadTest.setName("线程1:");
threadTest1.setName("线程2:");
//调用start()方法启动线程
threadTest.start();
threadTest1.start();
}
}
2.实现Runnable接口
①创建一个Runnable接口的实现类
②实现Runnable接口中的虚拟方法run(),方法体中写线程执行的操作
③以该实现类对象为参数传入Thread类的构造器中,创建Thread类的对象,
并通过Thread类的对象调用start()方法去启动该线程,调用该线程的run()方法
//创建一个是实现Runnable接口的实现类
class ThreadTest implements Runnable{
//实现接口中的run()方法
@Override
public void run() {
for (int i = 0; i < 20; i++) {
System.out.println(Thread.currentThread().getName() + ":" + i);
}
}
}
public class Textkang {
public static void main(String[] args) {
//创建实现类对象
ThreadTest threadTest = new ThreadTest();
//创建新线程
Thread thread1 = new Thread(threadTest);
Thread thread2 = new Thread(threadTest);
//设置线程名字
thread1.setName("线程一:");
thread2.setName("线程二:");
//调用start()方法启动线程
thread1.start();
thread2.start();
}
}
多线程操作共享数据时,有时会出现线程安全的问题。
//创建一个销售商品的类,继承于Thread类
class Sale extends Thread{
//设置商品数量为50个
private static int commodity = 50;
@Override
public void run() {
for(;;){
if(commodity > 0){
System.out.println(getName() + ":商品为:" + commodity + "号");
commodity--;
}else {
break;
}
}
}
}
public class SaleTest {
public static void main(String[] args) {
//设置三个售货口一起销售一批商品
Sale sale1 = new Sale();
Sale sale2 = new Sale();
Sale sale3 = new Sale();
//设置售货口名字
sale1.setName("售货口1:");
sale2.setName("售货口2:");
sale3.setName("售货口3:");
sale1.start();
sale2.start();
sale3.start();
}
}
这里没用做啥处理,所以出现了线程安全问题,比如同一个货物被三个窗口售卖,这显然是不被允许的。
同样用实现Runnable接口的方式如果不做处理,一样可能出现线程安全问题。
这里就不上代码了。
Java中通过同步机制处理线程安全问题,关键字:synchronized
同步机制中的同步监视器(也称“锁”)必须要唯一。
一、使用同步方法的理解
使用同步方法:①当使用继承于Thread类的方式创建线程,开多个线程时,就要new多个子类的对象,操作共享数据时,用同步方法处 理线程安全问题,那么同步方法就要声明为静态的,多个对象共享一个同步方法(同步方法的锁虽然没有声明出来,其实也是存在的,其实就是当前类充当的锁,类只加载一次,符合唯一性。)。即多个线程共抢一个锁,哪个线程抢到锁,就执行下去,其他线程则等待锁被释放。
//创建一个销售商品的类,继承于Thread类
class Sale extends Thread {
//设置商品数量为50个
private static int commodity = 500;
@Override
public void run() {
for(;;) {
sales();
}
}
public static synchronized void sales() {//这里的同步监视器是该类本身 Sale.class
if (commodity > 0) {
try {
sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "商品为:" + commodity + "号");
commodity--;
}
}
}
public class SaleTest {
public static void main(String[] args) {
//设置三个售货口一起销售一批商品
Sale sale1 = new Sale();
Sale sale2 = new Sale();
Sale sale3 = new Sale();
//设置售货口名字
sale1.setName("售货口1:");
sale2.setName("售货口2:");
sale3.setName("售货口3:");
sale1.start();
sale2.start();
sale3.start();
}
}
结果由于是竖着的,不好截图,就没弄。可以复制代码上机验证。
②当使用实现Runnable接口的方式传进线程,开多个线程,也只new了一个实现类对象,操作共享数据时,用同步方法 处理线程安全问题的话,同步方法不用声明为静态的,抢锁同上。
//创建一个销售商品的类,实现Runnable接口
class Sale implements Runnable {
//设置商品数量为50个
private static int commodity = 500;
@Override
public void run() {
for(;;) {
sales();
}
}
public static synchronized void sales() {//这里的同步监视器是该类本身 Sale.class
if (commodity > 0) {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "商品为:" + commodity + "号");
commodity--;
}
}
}
public class SaleTest {
public static void main(String[] args) {
//设置三个售货口一起销售一批商品
Sale sale = new Sale();
Thread sale1 = new Thread(sale);
Thread sale2 = new Thread(sale);
Thread sale3 = new Thread(sale);
//设置售货口名字
sale1.setName("售货口1:");
sale2.setName("售货口2:");
sale3.setName("售货口3:");
sale1.start();
sale2.start();
sale3.start();
}
}
二、使用同步代码块理解
使用同步代码块:①当使用当使用继承于Thread类的方式创建线程,开多个线程时,就要new多个子类的对象,操作共享数据时,用同步代码块的话,因为多个对象,每个对象都有自己的run()方法,同步代码块在run()方法中,也有多个,但是锁是唯一的,即不管有多少个run()方法,只有一个锁,当某个线程抢到锁,它就顺利执行,其他线程则等待锁被释放。
//创建一个销售商品的类,继承于Thread类
class Sale extends Thread{
//设置商品数量为50个
private static int commodity = 50;
@Override
public void run() {
for(;;){
synchronized(Sale.class) {//这里是用的该类的类名作为的同步监视器,
// 也可用其他类的对象作为同步监视器,切记同步监视器要唯一
if (commodity > 0) {
System.out.print(getName() + "商品为:" + commodity + "号");
commodity--;
} else {
break;
}
}
}
}
}
public class SaleTest {
public static void main(String[] args) {
//设置三个售货口一起销售一批商品
Sale sale1 = new Sale();
Sale sale2 = new Sale();
Sale sale3 = new Sale();
//设置售货口名字
sale1.setName("售货口1:");
sale2.setName("售货口2:");
sale3.setName("售货口3:");
sale1.start();
sale2.start();
sale3.start();
}
}
②当使用实现Runnable接口的方式传进线程,开多个线程,也只new了一个实现类对象,操作共享数据时,用同步代码块的话,由于只创建了一个实现类对象,那么run()方法就只有一份,同步代码块也只有一份,依然是哪个线程抢到锁,就执行,其他线程等待。
//创建一个销售商品的类,实现Runnable接口
class Sale implements Runnable {
//设置商品数量为50个
private static int commodity = 500;
@Override
public void run() {
for(;;) {
synchronized (this){//这里使用的是该对象作为的同步监视器,
// 还可以用其他的对象做同步监视器,保证唯一性就行
if(commodity > 0) {
try {
Thread.sleep(15);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "商品为:" + commodity + "号");
commodity--;
}else {
break;
}
}
}
}
}
public class SaleTest {
public static void main(String[] args) {
//设置三个售货口一起销售一批商品
Sale sale = new Sale();
Thread sale1 = new Thread(sale);
Thread sale2 = new Thread(sale);
Thread sale3 = new Thread(sale);
//设置售货口名字
sale1.setName("售货口1:");
sale2.setName("售货口2:");
sale3.setName("售货口3:");
sale1.start();
sale2.start();
sale3.start();
}
}