一.实现多线程的两种方式
1.通过继承Thread类实现多线程
/**
* 多线程:通过继承Thread类实现多线程
* 1.继承Thread类
* 2.重写run方法(作用:完成线程的业务逻辑代码)
* 3.创建当前线程类对象
* 4.调用start方法开始线程
* 优势:可以直接使用Thread类中的方法,代码简单
* 劣势:如果已经有了父类,就不能使用这种方法,因为java是单继承
*/
public class Demo01 extends Thread {
@Override
public void run() {
for (int i = 0;i<10;i++){
System.out.println(i);
}
}
public static void main(String[] args) {
Demo01 demo01 = new Demo01();
demo01.start();
}
}
2.实现Runnable接口开启多线程
/**
* 实现Runnable接口开启多线程
* 1.实现Runnable接口
* 2.实现run方法(作用:完成线程的业务逻辑代码)
* 3.创建Thread线程对象(传入Runnable对象)
* 4.调用start方法开启线程
* 优势:即使自己定义的线程有父类也没有关系,因为有了父类也可以实现接口,而且接口可以多实现的
* 劣势: 不能直接使用Threa中的方法需要先获取到线程对象后,才能的=得到Thread的方法,代码复杂
*/
public class Demo02 implements Runnable {
public void run() {
for (int a = 0 ; a<10 ; a++){
System.out.println(a);
}
}
public static void main(String[] args) {
Thread thread = new Thread(new Demo02());
thread.start();
}
}
二.匿名内部类实现多线程
/**
* 匿名内部类多线程写法
*/
public class Demo03 {
public static void main(String[] args) {
//1.重写run方法
new Thread(){
@Override
public void run() {
for (int a = 0 ; a<10 ;a++){
System.out.println(a);
}
}
}.start();
//实现Runna接口
new Thread(new Runnable() {
public void run() {
for (int a = 10 ; a<20 ;a++){
System.out.println(a);
}
}
}).start();
}s
}
三.安全问题与synchronized
1.synchronized是Java中解决并发问题的一种最常用最简单的方法 ,他可以确保线程互斥的访问同步代码
2.在并发编程中存在线程安全问题,主要原因有:1.存在共享数据 2.多线程共同操作共享数据。关键字synchronized可以保证在同一时刻,只有一个线程可以执行某个方法或某个代码块,同时synchronized可以保证一个线程的变化可见(可见性)
3.买票实例
package cn.msg.thread;
public class Demo04 {
public static void main(String[] args) {
new Test().start();
new Test().start();
}
}
class Test extends Thread{
//总票数
private static int tiket = 10;
private static Object flag = "a";
@Override
public void run() {
while (true){
//synchronized可以保证在同一时刻,只有一个线程可以执行某个方法或某个代码块
//当多个线程访问同一个对象的同一个方法时需要使用synchronized
synchronized (Test.class){
if (tiket<=0){
break;
}
System.out.println(this.getName()+"剩余"+tiket--+"张票");
try {
Thread.sleep(20);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
分析:将synchronized作用于一个给定的实例对象Test.class ,即当前实例对象就是锁对象,每次当线程进入synchronized包裹的代码块时就会要求当前线程持有Test.class 实例对象锁,如果当前有其他线程正持有该对象锁,那么新到的线程就必须等待,这样也就保证了每次只有一个线程执行tiket–;操作。当然除了Test.class作为对象外,我们还可以使用flag(static Object 修饰的属性)属性代替