Java线程总结---第一天

时间:2023-03-09 18:33:27
Java线程总结---第一天

  线程和进程各自有什么区别和优劣:
  
  进程是资源分配的最小单位,线程是程序执行的最小单位
  
  进程有自己的独立地址空间,每启动一个进程,系统就会为它分配地址空间,建立数据表来维护代码段、堆栈段和数据段,这种操作非常昂贵。而线程是共享进程中的数据的,使用相同的地址空间,因此CPU切换一个线程的花费远比进程要小很多,同时创建一个线程的开销也比进程要小很多,线程的上下文切换的性能消耗要小于进程。
  
  线程之间的通信更方便,同一进程下的线程共享全局变量、静态变量等数据。
  
  多进程程序更健壮,多线程程序只要有一个线程死掉,整个进程也死掉了,而一个进程死掉并不会对另外一个进程造成影响,因为进程有自己独立的地址空间。
  
  并行与并发
  
  并发是没有时间上的重叠的,两个任务是交替执行的,由于切换的非常快,对于外界调用者来说相当于同一时刻多个任务一起执行了;而并行可以看到时间上是由重叠的,也就是说并行才是真正意义上的同一时刻可以有多个任务同时执行。
  
  小程序demo
  
  public class Demo extends Thread {
  
  @Override
  
  public void run() {
  
  while (true){
  
  System.out.println(this.currentThread().getName());
  
  }
  
  }
  
  public static void main(String[] args) {
  
  Demo demo= new Demo();
  
  demo.start();
  
  }
  
  }
  
  public class Demo implements Runnable {
  
  @Override
  
  public void run() {
  
  while (true){
  
  System.out.println("hello thread");
  
  }
  
  }
  
  public static void main(String[] args) {
  
  Demo demo= new Demo();
  
  Thread thread=new Thread(demo,"t1");
  
  thread.start();
  
  }
  
  }
  
  public class Demo{
  
  public int count = 0;
  
  public void print() {
  
  while (true){
  
  System.out.println(count++);
  
  }
  
  }
  
  public static void main(String[] args) {
  
  new Thread(new Runnable() {
  
  @Override
  
  public void run() {
  
  new Demo().print();
  
  }
  
  }).start();
  
  new Thread(new Runnable() {
  
  @Override
  
  public void run() {
  
  new Demo().print();
  
  }
  
  }).start();
  
  }
  
  }
  
  synchronized锁范围
  
  普通同步方法,锁是当前实例对象
  
  静态同步方法,锁是当前类的class对象
  
  同步方法块,锁是括号里面的对象
  
  public class Demo{
  
  public synchronized void synsMethod1(){
  
  System.out.println("method1---");
  
  try {
  
  Thread.sleep(5000);
  
  } catch (InterruptedException e) {
  
  e.printStackTrace();
  
  }
  
  synsMethod3();
  
  }
  
  public synchronized void synsMethod2(){
  
  System.out.println("method2---");
  
  }
  
  public synchronized void synsMethod3(){
  
  System.out.println(www.yaxingyule.cn"method3---");
  
  }
  
  public static void main(String[] args) {
  
  Demo demo=new Demo( www.yingxionghui1.cn);
  
  new Thread(new Runnable() {
  
  @Override
  
  public void run() {
  
  demo.synsMethod1();
  
  }
  
  }).start();
  
  new Thread(new Runnable() {
  
  @Override
  
  public void run(www.tiaotiaoylzc.com) {
  
  demo.synsMethod2();
  
  }
  
  }).start();
  
  }
  
  }
  
  输出结果
  
  method1---
  
  method3---
  
  method2---
  
  再如下
  
  public class Demo{
  
  public synchronized void synsMethod1(){
  
  System.out.println("method1---");
  
  try {
  
  Thread.sleep(5000);
  
  } catch (InterruptedException e) {
  
  e.printStackTrace(www.mytxyl1.com);
  
  }
  
  synsMethod3();
  
  }
  
  public synchronized www.dituyule.org static void synsMethod2(){
  
  System.out.println("method2-www.yongshi123.cn--");
  
  }
  
  public synchronized void synsMethod3(){
  
  System.out.println("method3---");
  
  }
  
  public static void main(String[] args) {
  
  Demo demo=new Demo();
  
  new Thread(new Runnable() {
  
  @Override
  
  public void run() {
  
  demo.synsMethod1();
  
  }
  
  }).start();
  
  new Thread(new Runnable() {
  
  @Override
  
  public void run(www.ouyi3pt1.cn) {
  
  demo.synsMethod2();
  
  }
  
  }).start();
  
  }
  
  }
  
  输出结果(其中一种可能)
  
  method1---
  
  method2---
  
  method3---
  
  所以说静态方法的锁和非静态方法的锁是不一样的
  
  Synchronized锁重入
  
  关键字Synchronized拥有锁重入的功能,也就是在使用Synchronized的时候, 当一个线程得到一个对象的锁后,在该锁里执行代码的时候可以再次请求该对象的锁 时可以再次得到该对象的锁。
  
  当线程请求一个由其它线程持有的对象锁时,该线程会阻塞,而当线程请求由自己持有的对象锁时,如果该锁是重入锁,请求就会成功,否则阻塞。
  
  一个简单的例子就是:在一个Synchronized修饰的方法或代码块的内部调用本 类的其他Synchronized修饰的方法或代码块时,是永远可以得到锁的。
  
  /**
  
  * @program: demo
  
  * @description:
  
  * @author: lee
  
  * @create: 2019-02-25
  
  **/
  
  public class Demo{
  
  public synchronized www.yigouyule2.cn void synsMethod1(){
  
  System.out.println("method1---");
  
  try {
  
  Thread.sleep(1000);
  
  } catch (InterruptedException e) {
  
  e.printStackTrace();
  
  }
  
  synsMethod2();
  
  }
  
  public synchronized void synsMethod2(){
  
  System.out.println("method2---");
  
  }
  
  public static void main(String[] args) {
  
  Demo demo=new Demo();
  
  new Thread(new Runnable() {
  
  @Override
  
  public void run() {
  
  demo.synsMethod1();
  
  }
  
  }).start();
  
  new Thread(new Runnable() {
  
  @Override
  
  public void run() {
  
  demo.synsMethod2();
  
  }
  
  }).start();
  
  }
  
  }
  
  为什么要引入可重入锁这种机制哪?
  
  假如有一个线程T获得了对象A的锁,那么该线程T如果在未释放前再次请求该对象的锁时,如果没有可重入锁的机制,是不会获取到锁的,这样的话就会出现死锁的情况,所以最大的作用是避免死锁。
  
  volatile与synchronized的
  
  volatile关键字的作用就是强制从公共堆栈中取得变量的值,而不是线程私有的数据栈中取得变量的值。
  
  关键字volatile是线程同步的轻量级实现,性能比synchronized要好,并且volatile只能修饰变量,而synchronized可以修饰方法,代码块等。
  
  多线程访问volatile不会发生阻塞,而synchronized会发生阻塞。
  
  volatile可以保证数据的可见性,但不可以保证原子性(不是线程安全的),而synchronized可以保证原子性,也可以间接保证可见性,因为他会将私有内存和公共内存中的数据做同步。
  
  volatile解决的是变量在多个线程之间的可见性,而synchronized解决的是多个线程之间访问资源的同步性。
  
  ThreadLocal
  
  ThreadLocal提供了线程的局部变量,每个线程都可以通过set()和get()来对这个局部变量进行操作,但不会和其他线程的局部变量进行冲突,实现了线程的数据隔离。
  
  简要言之:往ThreadLocal中填充的变量属于当前线程,该变量对其他线程而言是隔离的。
  
  最典型的应用
  
  import java.sql.Connection;
  
  import java.sql.DriverManager;
  
  import java.sql.ResultSet;
  
  import java.sql.SQLException;
  
  import java.sql.Statement;
  
  /**
  
  * 采用ThreadLocal封装Connection
  
  *
  
  * @author Administrator
  
  *
  
  */
  
  public class ConnectionManager {
  
  //定义ThreadLocal静态变量,确定存取类型为Connection
  
  private static ThreadLocal<Connection> connectionHolder = new ThreadLocal<Connection>();
  
  /**
  
   * 得到Connection
  
   * @return
  
   */
  
  public static Connection getConnection() {
  
  Connection conn = connectionHolder.get();
  
  //如果在当前线程中没有绑定相应的Connection
  
  if (conn == null) {
  
  try {
  
  Class.forName("oracle.jdbc.driver.OracleDriver");
  
  String url = "jdbc:oracle:thin:@localhost:1521:bjpowern";
  
  String username = "drp1";
  
  String password = "drp1";
  
  conn = DriverManager.getConnection(url, username, password);
  
  //将Connection设置到ThreadLocal
  
  connectionHolder.set(conn);
  
  } catch (ClassNotFoundException e) {
  
  e.printStackTrace();
  
  } catch (SQLException e) {
  
  e.printStackTrace();
  
  }
  
  }
  
  return conn;
  
  }
  
  /**
  
   * 关闭数据库连接方法
  
   * @return
  
   */
  
  public static void closeConnection() {
  
  Connection conn = connectionHolder.get();
  
  if (conn != null) {
  
  try {
  
  conn.close();
  
  //从ThreadLocal中清除Connection
  
  connectionHolder.remove();
  
  } catch (SQLException e) {
  
  e.printStackTrace();
  
  }
  
  }
  
  }
  
  /**
  
   * 关闭数据库连接方法
  
   * @return
  
   */
  
  public static void close(Connection conn) {
  
  if (conn != null) {
  
  try {
  
  conn.close();
  
  } catch (SQLException e) {
  
  e.printStackTrace();
  
  }
  
  }
  
  }
  
  public static void close(Statement pstmt) {
  
  if (pstmt != null) {
  
  try {
  
  pstmt.close();
  
  } catch (SQLException e) {
  
  e.printStackTrace();
  
  }
  
  }
  
  }
  
  public static void close(ResultSet rs ) {
  
  if (rs != null) {
  
  try {
  
  rs.close();
  
  } catch (SQLException e) {
  
  e.printStackTrace();
  
  }
  
  }
  
  }
  
  /**
  
   * 事务开启
  
   * @return
  
   */
  
  public static void beginTransaction(Connection conn) {
  
  try {
  
  if (conn != null) {
  
  if (conn.getAutoCommit()) {
  
  conn.setAutoCommit(false); //手动提交
  
  }
  
  }
  
  }catch(SQLException e) {}
  
  }
  
  /**
  
   * 事务提交
  
   * @return
  
   */
  
  public static void commitTransaction(Connection conn) {
  
  try {
  
  if (conn != null) {
  
  if (!conn.getAutoCommit()) {
  
  conn.commit();
  
  }
  
  }
  
  }catch(SQLException e) {}
  
  }
  
  /**
  
   * 事务回滚
  
   * @return
  
   */
  
  public static void rollbackTransaction(Connection conn) {
  
  try {
  
  if (conn != null) {
  
  if (!conn.getAutoCommit()) {
  
  conn.rollback();
  
  }
  
  }
  
  }catch(SQLException e) {}
  
  }
  
  }
  
  初始化默认值
  
  public class ThreadlLocalDemo {
  
  /**
  
  * 初始化一个ThreadLocal对象,并且初始值设置为0
  
  */
  
  private static ThreadLocal<Integer> threadLocal = new ThreadLocal<Integer>() {
  
  @Override
  
  protected Integer initialValue() {
  
  return 0;
  
  }
  
  };
  
  public int getNextNumber() {
  
  threadLocal.set(threadLocal.get() + 1);
  
  return threadLocal.get();
  
  }
  
  static class DemoThread extends Thread {
  
  ThreadlLocalDemo threadlLocalDemo = null;
  
  public DemoThread(ThreadlLocalDemo localDemo) {
  
  this.threadlLocalDemo = localDemo;
  
  }
  
  @Override
  
  public void run() {
  
  for (int i = 0; i < 5; i++) { // 每个线程打印 5个值
  
  System.out.println("Thread:" + Thread.currentThread().getName()
  
  + ",threadlLocalDemo:" + threadlLocalDemo.getNextNumber());
  
  }
  
  }
  
  }
  
  public static void main(String[] args) {
  
  ThreadlLocalDemo threadlLocalDemo = new ThreadlLocalDemo();
  
  DemoThread demoThread = new DemoThread(threadlLocalDemo);
  
  DemoThread demoThread2 = new DemoThread(threadlLocalDemo);
  
  DemoThread demoThread3 = new DemoThread(threadlLocalDemo);
  
  DemoThread demoThread4 = new DemoThread(threadlLocalDemo);
  
  demoThread.start();
  
  demoThread2.start();
  
  demoThread3.start();
  
  demoThread4.start();
  
  }
  
  }
  
  ThreadLocal实现的原理
  
  首先,我们来看一下ThreadLocal的set()方法,因为我们一般使用都是new完对象,就往里边set对象了
  
  public void set(T value) {
  
  Thread t = Thread.currentThread();
  
  ThreadLocalMap map = getMap(t);
  
  if (map != null)
  
  map.set(this, value);
  
  else
  
  createMap(t, value);
  
  }
  
  上面有个ThreadLocalMap,我们去看看这是什么?
  
  static class ThreadLocalMap {
  
  /**
  
  * The entries in this hash map extend WeakReference, using
  
  * its main ref field as the key (which is always a
  
  * ThreadLocal object). Note that null keys (i.e. entry.get()
  
  * == null) mean that the key is no longer referenced, so the
  
  * entry can be expunged from table. Such entries are referred to
  
  * as "stale entries" in the code that follows.
  
  */
  
  static class Entry extends WeakReference<ThreadLocal<?>> {
  
  /** The value associated with this ThreadLocal. */
  
  Object value;
  
  Entry(ThreadLocal<?> k, Object v) {
  
  super(k);
  
  value = v;
  
  }
  
  }
  
   /**
  
   * Construct a new map initially containing (firstKey, firstValue).
  
   * ThreadLocalMaps are constructed lazily, so we only create
  
   * one when we have at least one entry to put in it.
  
   */
  
   ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {
  
   table = new Entry[INITIAL_CAPACITY];
  
   int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
  
   table[i] = new Entry(firstKey, firstValue);
  
   size = 1;
  
   setThreshold(INITIAL_CAPACITY);
  
   }
  
  //....很长
  
  }
  
  通过上面我们可以发现的是ThreadLocalMap是ThreadLocal的一个内部类。用Entry类来进行存储
  
  我们的值都是存储到这个Map上的,key是当前ThreadLocal对象!
  
  如果该Map不存在,则初始化一个:
  
  void createMap(Thread t, T firstValue) {
  
  t.threadLocals = new ThreadLocalMap(this, firstValue);
  
  }
  
  如果map存在
  
  /**
  
  * Get the map associated with a ThreadLocal. Overridden in
  
  * InheritableThreadLocal.
  
  *
  
  * @param t the current thread
  
  * @return the map
  
  */
  
  ThreadLocalMap getMap(Thread t) {
  
  return t.threadLocals;
  
  }
  
  Thread维护了ThreadLocalMap变量
  
  /* ThreadLocal values pertaining to this thread. This map is maintained
  
  * by the ThreadLocal class. */
  
  ThreadLocal.ThreadLocalMap threadLocals = null
  
  从上面又可以看出,ThreadLocalMap是在ThreadLocal中使用内部类来编写的,但对象的引用是在Thread中! 于是我们可以总结出:Thread为每个线程维护了ThreadLocalMap这么一个Map,而ThreadLocalMap的key是LocalThread对象本身,value则是要存储的对象。
  
  有了上面的基础,我们看get()方法就一点都不难理解了:
  
  public T get() {
  
  Thread t = Thread.currentThread();
  
  ThreadLocalMap map = getMap(t);
  
  if (map != null) {
  
  ThreadLocalMap.Entry e = map.getEntry(this);
  
  if (e != null) {
  
  @SuppressWarnings("unchecked")
  
  T result = (T)e.value;
  
  return result;
  
  }
  
  }
  
  return setInitialValue();
  
  }
  
  ThreadLocal原理总结
  
  每个Thread维护着一个ThreadLocalMap的引用
  
  ThreadLocalMap是ThreadLocal的内部类,用Entry来进行存储
  
  调用ThreadLocal的set()方法时,实际上就是往ThreadLocalMap设置值,key是ThreadLocal对象,值是传递进来的对象
  
  调用ThreadLocal的get()方法时,实际上就是往ThreadLocalMap获取值,key是ThreadLocal对象
  
  ThreadLocal本身并不存储值,它只是作为一个key来让线程从ThreadLocalMap获取value。