1多线程与进程
以扫雷游戏说明一个进程中的多个线程:
一个计时器,一个点点点
多线程的目的是为了提高应用程序的使用率。
(程序的线程越多,抢到的几率就越高)
Java程序的运行原理及JVM的启动是多线程的吗?
A:Java命令去启动JVM,JVM会启动一个进程,该进程会启动一个主线程。然后主线程去调用某个类的 main 方法。所以 main方法运行在主线程中。在此之前的所有程序都是单线程的。
B:JVM的启动是多线程的,因为它最低有两个线程启动了,主线程和垃圾回收线程。
2多线程的实现方法
方法一:继承Thread类。
A:自定义类MyThread继承Thread类。
B:MyThread类里面重写run()?
为什么是run()方法呢?
C:创建对象
D:启动线程
该类要重写run()方法,为什么呢?
不是类中的所有代码都需要被线程执行的。
而这个时候,为了区分哪些代码能够被线程执行,java提供了Thread类中的run()用来包含那些被线程执行的代码。
run()和start()的区别?
run():仅仅是封装被线程执行的代码,直接调用是普通方法
start():首先启动了线程,然后再由jvm去调用该线程的run()方法。
public final String getName():获取线程的名称。
public final void setName(String name):设置线程的名称
针对不是Thread类的子类中如何获取线程对象名称呢?
public static Thread currentThread():返回当前正在执行的线程对象
如果想使用setName(), getName()。需要写构造函数:
public MyThread() {
}
public MyThread(String name){
super(name);
}
* public final int getPriority():返回线程对象的优先级
* public final void setPriority(int newPriority):更改线程的优先级。
public static void main(String[] args) {
ThreadPriority tp1 = new ThreadPriority();
ThreadPriority tp2 = new ThreadPriority();
ThreadPriority tp3 = new ThreadPriority();
tp1.setName("东方不败");
tp2.setName("岳不群");
tp3.setName("林平之");
tp1.setPriority(10);
tp2.setPriority(1);
tp1.start();
tp2.start();
tp3.start();
}
方法二:实现Runnable接口
如果一个类已经有一个父类了,还想实现多线程怎么办。
适合多个程序相同的代码去处理同一个资源的情况,把线程同程序的代码,数据有效分离
* A:自定义类MyRunnable实现Runnable接口
* B:重写run()方法
* C:创建MyRunnable类的对象
* D:创建Thread类的对象,并把C步骤的对象作为构造参数传递
public static void main(String[] args) {
MyRunnable my = new MyRunnable();
Thread t1 = new Thread(my, "林青霞");
Thread t2 = new Thread(my, "刘意");
t1.start();
t2.start();
}
3线程的控制和生命周期
4多线程安全问题的原因
也是我们以后判断一个程序是否有线程安全问题的依据
A:是否有多线程环境
B:是否有共享数据
C:是否有多条语句操作共享数据
5 同步解决线程安全问题
A:同步代码块
synchronized(对象) {
需要被同步的代码;
}
这里的锁对象可以是任意对象。
售票案例:
public static void main(String[] args) {
SellTicket st = new SellTicket();
Thread t1 = new Thread(st, "窗口1");
Thread t2 = new Thread(st, "窗口2");
Thread t3 = new Thread(st, "窗口3");
t1.start();
t2.start();
t3.start();
}
public class SellTicket implements Runnable{
private int tickets = 100;
private Object obj = new Object();
public void run(){
while(true){
synchronized(obj){
if(tickets > 0){
System.out.println(Thread.getCurrentThread().getName() + " " + tickets--);
}
}
}
}
}
B: 同步方法
把同步加在方法上。(这里的锁对象是this)
C:静态同步方法
把同步加在静态方法上。(这里的锁对象是当前类的字节码文件对象(反射讲了字节码文件对象)
public class SellTicket implements Runnable {
private static int tickets = 100;
private Object obj = new Object();
private Demo d = new Demo();
private int x = 0;
@Override
public void run() {
while (true) {
if(x%2==0){
synchronized (SellTicket.class) {
if (tickets > 0) {
System.out.println(Thread.currentThread().getName()
+ "正在出售第" + (tickets--) + "张票 ");
}
}
}else {
// synchronized (d) {
// if (tickets > 0) {
// System.out.println(Thread.currentThread().getName()
// + "正在出售第" + (tickets--) + "张票 ");
// }
// }
sellTicket();
}
x++;
}
}
// private void sellTicket() {
// synchronized (d) {
// if (tickets > 0) {
// System.out.println(Thread.currentThread().getName()
// + "正在出售第" + (tickets--) + "张票 ");
// }
// }
// }
//如果一个方法一进去就看到了代码被同步了,那么可以把这个同步加在方法上
// private synchronized void sellTicket() {
// if (tickets > 0) {
// System.out.println(Thread.currentThread().getName()
// + "正在出售第" + (tickets--) + "张票 ");
// }
// }
private static synchronized void sellTicket() {
if (tickets > 0) {
System.out.println(Thread.currentThread().getName()
+ "正在出售第" + (tickets--) + "张票 ");
}
}
}
class Demo {
}
这个代码描述了三种同步方法。并且可以看出如何从一种方法转换成另一种。所以很好。即使很久没看,再看这个代码也可以想起来之前的思路。
只要是同一个锁的代码,不管里面内容是否相同,都会被锁住(当然这是极端的情况。事实上应该是为了需求而上锁的)
6以前讲过的线程安全的类
A:StringBuffer(因为它几乎所有方法都是方法加锁的。线程安全的类效率比较低。因为每次要对对象是否被锁上进行判断)
B:Vector
C:Hashtable
D:如何把一个线程不安全的集合类变成一个线程安全的集合类
用Collections工具类的方法即可。
// public static <T> List<T> synchronizedList(List<T> list)
List<String> list1 = new ArrayList<String>();// 线程不安全
List<String> list2 = Collections
.synchronizedList(new ArrayList<String>()); // 线程安全