线程 线程与进程的区别 并行和并发的区别 线程安全sychronized 等待唤醒机制

1.

什么是进程?

通过查看任务管理器,发现正在运行的程序就是一个进程!
多进程有什么意义呢?
现在的计算机是一个多进程计算机,在去做一件事情的同时可以做另一件事情;提高CPU的使用率

我们在打游戏同时在听音乐,这两个进程是同时发生的吗?

打游戏------->开启了游戏进程
听音乐------>开启了音乐进程
不是同时在进行,感觉他们在同时进行,这个因为CPU在两者之间进行着高效的切换(抢占CPU的执行权)

2

什么是线程?

线程是依赖于进程存在的:把线程可以看作是进程中的某一个任务,比如迅雷软件,扫雷(开始--->玩的同时--->计时)

CPU的执行权:具有随机性! (每一个线程在抢占CPU的执行权具有随机性)

     单线程:程序的执行路径只有一条路径
     多线程:程序的执行路径有多条
  
  

3.

并行和并发:同时的意思

  前者是逻辑上的同时,指的是在同一个时间内(时间段)同时发生
  后者是物理上的同时,指的是在同一个时间点(10:30:30进行开抢:购物商城...)上同时发生
  
  面试题:
  jvm:java虚拟机是多线程的吗?
  程序能够被执行:是因为里面main()方法,main()被Jvm所识别,启动Jvm,实质启动了
  主线程(main方法所在的就是主线程),通过刚才这个代码:两个输出语句之间做了多次创建对象,
  程序依然能够执行打印出来,java语言会自动启动垃圾回收器,----->垃圾回收线程,所有能够保证有时候不会内存溢出
  
  java虚拟机是一个多线程,至少开启了两个线程:
  第一个:main():主线程 Thread main
  第二个:垃圾回收线程,保证及时的回收不用的对象

4.

创建新执行线程有两种方法。

           一种方法是 将类声明为 Thread 的子类。该子类应重写 Thread 类的 run 方法。接下来可以分配并启动该子类的实例。
    第一种实现多线程程序的开发步骤:
  1:自定义一个类,该类继承自Thread类,
  2:重写该类中的run()方法
  3:主线程中去创建该类的对象
  4:启动线程.

//1)自定义该类继承自了一个Thread类
//2):重写run()方法
//为什么要重写run()方法呢?
//该方法里面需要执行一些耗时的操作:循环语句.../sleep()
public class MyThread extends Thread{

@Override
public void run() {
for(int x = 0; x<10; x++){
System.out.print(x);
}
}


}


public static void main(String[] args) {
MyThread m1 = new MyThread();
MyThread m2 = new MyThread();

m1.start();//启动线程:start()方法,调用start()其实是让Jvm去调用线程的run 方法()来实现执行多线程的一种随机性
m2.start();
}




如何获取线程的名称呢?
   public final String getName()返回该线程的名称。
   public final void setName(String name)改变线程名称,使之与参数 name 相同


    public static Thread currentThread()返回对当前正在执行的线程对象的引用。 
    System.out.println(Thread.currentThread().getName());//main :获取当前正在发生的线程的名称 




5.

线程里面常用的方法



1
 public final void join():等待该线程终止
         t1.join();//等待t1线程结束后  t2 t3 线程才会开始


2  

public static void sleep(long millis)在指定的毫秒数内让当前正在执行的线程休眠(暂停执行)


3
public final void stop():强制停止执行
public void interrupt()中断线程。  中断当前线程的这种状态(打破了一种状态)


public static void main(String[] args) {
ThreadStop t = new ThreadStop();
t.start();
//t.stop();//直接结束线程 什么都不执行 就停止在这里
try {
Thread.sleep(1000);//睡眠了一秒  然后被中断了   
t.interrupt();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}

}




4.
public static void yield()暂停当前正在执行的线程对象,并执行其他线程。
  
 线程的执行具有随机性,并且不能保证该线程永远暂停,可能抢占到了CPU的执行权就会执行它...


第二种


  API的描述:
  创建线程的另一种方法是声明实现 Runnable 接口的类
  该类然后实现 run 方法。然后可以分配该类的实例,在创建 Thread 时作为一个参数来传递并启动
 
  实现方式2的开发步骤:
  1)自定义一个类MyRunnable(同一个资源)实现Runnable接口
  2)然后实现Rannable里面的run方法:耗时的操作
  3)在主线程中创建MyRuhnable类的对象
  4)创建Thread类对象,将第三步的对象作为参数进行传递来启动线程



 
public static void main(String[] args) {
MyRunnable m = new MyRunnable();//创建MyRunnable对象  作为同一个资源
//创建线程对象
Thread t1 = new Thread(m,"线程一");
Thread t2 = new Thread(m,"线程二");


//Thread(Runnable target, String name) 
t1.start();
t2.start();

}



6.

线程安全的检测条件:

  1)看我们当前的环境是否属于多线程程序
  2)当前的多线程程序中是否有共享数据
  3)是否多条语句对共性数据进行操作
如何让线程安全?
  要实现线程安全,那么就必须改变第三个条件(前两者不能改变),
  将对共享数据的操作的多条语句包装起来,怎么包装:java提供了一个同步机制 :sychronized(是一个关键字)
         同步机制的使用:
  sychronized(对象){ 
  多条语句对共享数据进行操作;
  }

同步机制:每一个线程只能使用同一个对象(sychronized当作一个锁:使用的是同一把锁)


同步代码快锁对象是可以什么样的对象?
  可以是任意的对象(重点)
 
  
	public class SeilTiker implements Runnable {
		int ticket = 100;
		Object obj = new Object();


		@Override
		public void run() {
			while (true) {// 同步代码块
				synchronized (obj) {
					if (ticket > 0) {
						try {
							Thread.sleep(1000);
						} catch (InterruptedException e) {
							e.printStackTrace();
						}


						System.out.println(Thread.currentThread().getName()
								+ "正在出售第" + (ticket--));
					}
				}
			}


		}
	}

如果一个方法进来之后是同步代码块,那么该方法就可以定义为同步方法,他的锁对象是this
静态的同步方法的锁对象是:

类名.class java中的反射机制


//线程安全,那么和线程安全相关的类有哪些呢?
StringBuffer sb = new StringBuffer() ;

//Vecotr:List的子实现类
Vector<String> v = new Vector<String>() ;

//Hashtable
Hashtable<String, String> hs = new Hashtable<String,String>() ;

//Vector:线程安全:一般不使用它
//public static <T> List<T> synchronizedList(List<T> list)
//返回指定列表支持的同步(线程安全的)列表

List<String> list = new ArrayList<String>() ;
//也可以实现线程安全的操作!
List<String> list2 = Collections.synchronizedList(new ArrayList<String>()) ;

}

}

7.

分析:当前的资源情况:
  Student: 共同的资源
  setThread:设置学生数据(生成者)
  getThread:获取学生数据(消费者)
  StudentDemo(测试类)
  
  问题1:
  按照生产消费模式:分别进行产生数据和消费数据,通过测试打印出来:
  null---0
  
  线程的通信:两个或者两个以上的线程只能针对同一资源进行操作,
  优化:改进:通过在成员变量的位置:创建一个学生对象,吧作为有参的参数进行传递
  
 改进:给每一个线程加入while循环,让在控制出现多个数据,
  改进之后出现的问题:
  1)同一数据打印了多次
  CPU的一点点时间片,要执行很多次!
  2)姓名和年龄不符
  线程随机性导致的!
  
  针对这个问题:如何解决这种问题!
  检验线程安全问题的标准:
  1)是否是多线程环境
  2)是否有共享数据    
  3)是否有多条语句对共享数据进行操作
 
  
  解决方案:
  用同步代码块将多条语句对共享数据的操作包装起来
  加入同步机制
  注意:同步锁,不同的线程之间使用的是同一把锁对象!
  
  这种方案虽然解决了线程安全的问题,但是在控制台出现一大片数据,用户体验不好!
  如果想让控制一次的进行显示出来.
  如何解决:
  java提供了另一种机制: 等待唤醒机制
  
  Object类中提供了一些方法:
  wait():线程等待
  public final void notify():唤醒正在等待的单个线程
  public final void notifyAll()唤醒所有线程
  
  面试题:
  这几个方法都是线程有关的方法,为什么把这个方法不定一在Thread类里面?
  刚才这个案例,使用的锁对象进行调用,锁对象可以是任意对象!
  而Object本身就是代表所有的类根类:代表所有对象!

 


public class Student {
String name ;
int age; 
boolean flag;


}

public class Set implements Runnable {
		private Student s;

		public Set(Student s) {
			this.s = s;
		}

		private int x = 0;

		@Override
		public void run() {
			while (true) {
				synchronized (s) {
					if (s.flag == true) {
						try {// 当产生数据了 变为true Set线程在这等着 等Get把数据读完
							s.wait();
						} catch (InterruptedException e) {
							e.printStackTrace();
						}
					}
					if (x % 2 == 0) {
						s.name = "zhang";
						s.age = 19;
					} else {
						s.name = "gao";
						s.age = 21;
					}
					x++;
					s.flag = true;// 修改标记 告诉Get 有数据了 你过来取
					s.notify();// 唤醒Get线程
				}

			}
		}

	}

	public class Get implements Runnable {
		private Student s;

		public Get(Student s) {
			this.s = s;
		}

		@Override
		public void run() {
			while (true) {
				synchronized (s) {
					if (s.flag == false) {// 变为false时 等待Get把数据读完
						try {
							s.wait();// 等待读数据
						} catch (InterruptedException e) {
							e.printStackTrace();
						}
					}

					System.out.println(s.name + "--" + s.age);
					s.flag = false;// 当接收到数据 修改标记为false 告诉Set线程 我接收完数据了
					s.notify();// 唤醒Set线程
				}
			}
		}

	}

	public class Test {
		public static void main(String[] args) {
			Student s = new Student();// 创建同一个资源
			// 分别创建生产者和消费者线程
			Thread t1 = new Thread(new Set(s), "y");
			Thread t2 = new Thread(new Get(s), "t");
			t1.start();
			t2.start();
		}

	}

}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值