线程 是程序中的执行线程。Java 虚拟机允许应用程序并发地运行多个执行线程。
每个线程都有一个优先级,高优先级线程的执行优先于低优先级线程。每个线程都可以或不可以标记为一个守护程序。当某个线程中运行的代码创建一个新 Thread 对象时,该新线程的初始优先级被设定为创建线程的优先级,并且当且仅当创建线程是守护线程时,新线程才是守护程序。
进程:
1. 正在运行的程序,是系统运行和资源调度的独立单位。
2. 每一个进程都有自己的内存空间和系统资源。
3. 由系统开辟内存空间运行的程序。
线程:
1. 是进程中的单个顺序的控制流程是一条执行路径。
2. 一个进程如果只有一条执行路径称之为单线程。
3. 一个进程有多个执行路径称之为多线程。
4. 由进程开辟空间,运行在进程中的任务。
5. 一个进程至少有一个线程。
多线程:
(java中并没有多线程的东西,我们在Java使用的多线程其实是调用C/C++中写好的多线程的内容)
start() : 使该线程开始执行;Java 虚拟机调用该线程的 run 方法。
sleep(); 线程睡眠 毫秒数
setName()设置线程的名字
getName()获取线程的名字。
currentThread() : 返回对当前正在执行的线程对象的引用。
创建新执行线程有两种方法。
**方法一:**声明为 Thread 的子类。该子类应重写 Thread 类的 run 方法。
public class ThreadDemo1 extends Thread {
@Override
public void run() {
//模拟代码的复杂
for(int i =0;i<=200;i++){
//模拟网络延迟
try{
Thread.sleep(100);
//获取当前正在执行的线程的名字
System.out.println(Thread.currentThread().getName()+"----"+i);
}catch(InterruptedException e){
System.out.println("程序中断!");
}
}
}
}
(一般我们在多线程中去运行的代码比较复杂,简单的代码不会加入到多线程中去。)
**方法二:**声明实现 Runnable 接口的类。然后重写 run 方法。
public class TicketRunnable implements Runnable {
int num = 200;
@Override
public void run() {
for (int i =0;i<=200;i++){
try {
if(num > 0){
ThreadTest.sleep(100);
System.out.println(ThreadTest.currentThread().getName()
+ "正在售卖第"+ num + "张票" );
num --;
}
}catch (InterruptedException e){
System.out.println("程序中断!");
}
}
}
}
然后可以分配该类的实例,在创建 Thread 时作为一个参数来传递并启动
public class TicketRunnableTest {
public static void main(String[] args) throws Exception {
TicketRunnable ticketRunnable = new TicketRunnable();
Thread t1 = new Thread(ticketRunnable);
t1.setName("窗口一");
Thread t2 = new Thread(ticketRunnable);
t2.setName("窗口二");
t1.start();
t2.start();
}
}
线程的操作: 线程不能重复启动。
start 和 run的区别:
当调用了线程对象的start方法之后,该线程就进入了就绪状态,但是此时线程调度程序还没有把该线程设置为当前线程,此时处于就绪状态。在线程运行之后,从等待或者睡眠中回来之后,也会处于就绪状态。如果一个线程的run方法执行结束或者调用stop方法后,无法再使用start方法令其进入就绪。
yield() :线程礼让
stop(): 线程停止 太暴力了
sleep():线程休眠
join(): 线程等待终止
interrupted : 线程中断
wait(): 线程等待
notify: 线程唤醒
notifyAll()
线程安全问题
实例:模拟买火车票
在运行程序后,发现对同一资源共享,最后输出了0票?
1. 有资源共享
2. 多线程抢占统一资源。
3. 网络延迟
怎么解决:
加锁:java中关键字: synchronized 同步
我们还需要锁。
锁有这么几种情况
1. 任意锁 synchronized(任意对象) 解决不了安全问题
2. 使用this作为锁
int num = 200;
@Override
public void run() {
for (int i =0;i<=200;i++){
eat();
}
}
public void eat(){
//任意锁synchronized ("Hello")
//因为字符串是一个常量在常量池里只有一份,所以只有一把锁
//synchronized (new object)
//new object是对象,用一次new一次,锁不止一把所以不能解决问题
//同步(锁)(this)因为是当前类的实例化对象,只有一份,只有一个的可以解决线程安全问题
synchronized (this){
try {
if(num > 0){
Thread.sleep(50);
System.out.println(Thread.currentThread().getName()
+ "正在售卖第"+ num + "张票" );
num --;
}
}catch (InterruptedException e){
System.out.println("程序中断!");
}
}
}
//同步方法(实例化对象本身)
public synchronized void eat(){
//同步(锁)
try {
if(num > 0){
ThreadTest.sleep(50);
System.out.println(ThreadTest.currentThread().getName()
+ "正在售卖第"+ num + "张票" );
num --;
}
}catch (InterruptedException e){
System.out.println("程序中断!");
}
}
//静态同步方法的锁是什么?(运行时的类本身/二进制字节码文件对象)
public static synchronized void eat(){
//同步(锁)
try {
if(num > 0){
ThreadTest.sleep(50);
System.out.println(ThreadTest.currentThread().getName()
+ "正在售卖第"+ num + "张票" );
num --;
}
}catch (InterruptedException e){
System.out.println("程序中断!");
}
}
拓展:非静态同步方法和静态同步方法区别
所有的非静态同步方法用的都是同一把锁——实例对象本身
,也就是说如果一个实例对象的非静态同步方法获取锁后,该实例对象的其他非静态同步方法必须等待获取锁的方法释放锁后才能获取锁,可是别的实例对象的非静态同步方法因为跟该实例对象的非静态同步方法用的是不同的锁,所以毋须等待该实例对象已获取锁的非静态同步方法释放锁就可以获取他们自己的锁。
而所有的静态同步方法用的也是同一把锁——运行时的类本身
,这两把锁是两个不同的对象,所以静态同步方法与非静态同步方法之间是不会有竞态条件的。但是一旦一个静态同步方法获取锁后,其他的静态同步方法都必须等待该方法释放锁后才能获取锁,而不管是同一个实例对象的静态同步方法之间,还是不同的实例对象的静态同步方法之间,只要它们同一个类的实例对象!